﻿using Qing.Lang;
using Qing.Std;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Qing.Lang {
    /*
     * 对象类型的包装类
     * 本质上就是一个语境，继承了Ctx
     */
    public class Obj : Ctx {
        /*
         * 这里定义了一个raw，用来存放原生的对象
         * 如果需要封装C#原生的功能
         * 可以继承Obj类，把原始的对象放到raw里
         * 再添加对原始对象的操作方法
         * 相应的例子在Proto命名空间里
         */
        public object Raw { get; set; }

        public Obj() : base() {
            Raw = 0;
            InitByAnno();
        }

        public Obj(Ctx? ctx) : this() {
            Father = ctx;
            Raw = 0;
            InitByAnno();
        }
        public Obj(object raw) : this() {
            this.Raw = raw;
            InitByAnno();
        }

        public virtual string toStr(string tp = "") {
            if (Map.Count == 0) return tp +"｛｝";
            string objStr = tp + "｛";
            foreach (var item in Map) {
                objStr += item.Key + '：' + item.Value.ToStr() + '，';
            }
            return objStr + '｝';
        }

        public void InitByAnno() {
            foreach (var member in GetType().GetMembers()) {
                foreach (var anno in member.GetCustomAttributes(true)) {
                    if (anno is BindAnno) {
                        MemberInfo? target = (anno as BindAnno)!.GetTargetMember(member);
                        if (target != null && ( target is FieldInfo || target is PropertyInfo)) {
                            Type targetType;
                            if (target is FieldInfo) {
                                targetType = (target as FieldInfo)!.FieldType;
                            } else {
                                targetType = (target as PropertyInfo)!.PropertyType;
                            }
                             
                            if (targetType == (true).GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonBoolMemberProp(this, (anno as BindAnno)!, member));
                            } else if (targetType == 0.GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonIntMemberProp(this, (anno as BindAnno)!, member));
                            } else if (targetType == (0L).GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonIntMemberPropForLong(this, (anno as BindAnno)!, member));
                            } else if (targetType == ((decimal)0.0).GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonFloatMemberProp(this, (anno as BindAnno)!, member));
                            } else if (targetType == (0.0).GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonFloatMemberPropForDouble(this, (anno as BindAnno)!, member));
                            } else if (targetType == (0.0f).GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonFloatMemberPropForFloat(this, (anno as BindAnno)!, member));
                            } else if (targetType == ("").GetType()) {
                                Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, new CommonStringMemberProp(this, (anno as BindAnno)!, member));
                            }
                            
                            
                        }

                    }
                }
            }

            var subClassList = GetType().GetNestedTypes(System.Reflection.BindingFlags.NonPublic| System.Reflection.BindingFlags.Instance);
            foreach(var subClass in subClassList) {
                foreach (var anno in subClass.GetCustomAttributes(true)) {
                    if (anno is BindAnno) {
                        if (subClass.IsSubclassOf(typeof(Native))) {
                            Map[(anno as BindAnno)!.attr] = new Expr(TP.Native, Activator.CreateInstance(subClass)!);
                        } else if (subClass.IsSubclassOf(typeof(Prop))) {
                            Map[(anno as BindAnno)!.attr] = new Expr(TP.Prop, Activator.CreateInstance(subClass, this)!);
                        }
                        
                    }
                }
            }

        }

    }


    


    public abstract class Prop {

        public dynamic obj;

        public Prop(Obj obj) {
            this.obj = obj;
        }


        public abstract Expr Qget(Ctx? ctx=null);
        public abstract Expr Qset(Expr val, Ctx? ctx = null);

    }


    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = true)]
    public class BindAnno : System.Attribute {

        public string attr = "";
        public string subPath = "";
        public Type? realType = null;
        public bool noSetter = false; 

        public BindAnno(string name, bool noSetter=false) {
            this.attr = name;
            this.noSetter = noSetter;
        }

/*        public BindAnno(string name, string path, bool noSetter = false) {
            this.attr = name;
            this.subPath = path;
            this.noSetter = noSetter;
        }*/

        public BindAnno(string name, string path, Type tp, bool noSetter = false) {
            this.attr = name;
            this.subPath = path;
            this.realType = tp;
            this.noSetter = noSetter;
        }

        public MemberInfo? GetTargetMember(MemberInfo member) {
            if(subPath == null || subPath == "") {
                return member;
            } else {
                return GetSubAttr(member, subPath, realType);
            }

        }



        public static MemberInfo? GetSubAttr(MemberInfo member, string path, Type? realType) {
            if (path == null || path == "") {
                return null;
            }
            var pathList = path.Split('.');
            MemberInfo? curr = member;
            int idx = 0;
            foreach(var p in pathList) {             
                try {
                    if (idx == 0 && realType != null) {
                        curr = realType.GetMember(p)[0];
                    }else if (curr is FieldInfo) {
                        curr = (curr as FieldInfo)!.FieldType.GetMember(p)[0];
                    }else if(curr is PropertyInfo) {
                        curr = (curr as PropertyInfo)!.PropertyType.GetMember(p)[0];
                    } else {
                        throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
                    }
                }catch(Exception e) {
                    throw new Exception("获取对象成员的路径错误；原因：" + e.Message);
                }

                if(curr is null) {
                    break;
                }
                idx++;
            }
            return curr;
        }

        public object? GetTargetValue(object obj, MemberInfo member) {
            object? val = obj;
            if (member is FieldInfo) {
                val = (member as FieldInfo)!.GetValue(obj);
            }else if(member is PropertyInfo) {
                val = (member as PropertyInfo)!.GetValue(obj);
            } else {
                throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
            }

            if(string.IsNullOrEmpty(subPath)) {
                return val;
            } else {
                var pathList = subPath.Split(".");
                foreach(var p in pathList) {
                    if(val == null) {
                        break;
                    }
                    try {
                        var subMember = val.GetType().GetMember(p)[0];
                        if (subMember is FieldInfo) {
                            val = (subMember as FieldInfo)!.GetValue(val);
                        }else if(subMember is PropertyInfo) {
                            val = (subMember as PropertyInfo)!.GetValue(val);
                        } else {
                            throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
                        }
                    }catch(Exception e) {
                        throw new Exception("获取对象成员的路径错误；原因：" + e.Message);
                    }
                }
                return val;
            }
        }

        public void SetTargetValue(object obj, MemberInfo member, object v) {
            if (subPath == null || subPath == "") {
                if (member is FieldInfo) {
                    (member as FieldInfo)!.SetValue(obj, v);
                } else if (member is PropertyInfo) {
                    (member as PropertyInfo)!.SetValue(obj, v);
                } else {
                    throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
                }
            } else {
                object? val = obj;
                if (member is FieldInfo) {
                    val = (member as FieldInfo)!.GetValue(obj);
                } else if (member is PropertyInfo) {
                    val = (member as PropertyInfo)!.GetValue(obj);
                } else {
                    throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
                }

                var pathList = subPath.Split(".");
                int idx = 0;
                MemberInfo subMember = member;
                foreach (var p in pathList) {
                    if (val == null) {
                        throw new Exception("对绑定对象成员赋值的路径中出现空值");
                    }
                    if(idx == pathList.Length-1) {
                        if (subMember is FieldInfo) {
                            val = (subMember as FieldInfo)!.GetValue(obj);
                        } else if (subMember is PropertyInfo) {
                            val = (subMember as PropertyInfo)!.GetValue(obj);
                        } else {
                            throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
                        }
                        break;
                    }
                    try {
                        subMember = val.GetType().GetMember(p)[0];
                        if (subMember is FieldInfo) {
                            val = (subMember as FieldInfo)!.GetValue(val);
                        } else if (subMember is PropertyInfo) {
                            val = (subMember as PropertyInfo)!.GetValue(val);
                        } else {
                            throw new Exception("错误的对象成员绑定声明，仅用于字段或属性");
                        }
                    } catch (Exception e) {
                        throw new Exception("获取对象成员的路径错误；原因：" + e.Message);
                    }
                    idx++;
                }
            }
        }

    }

    


    public class CommonBoolMemberProp : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonBoolMemberProp(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member; 
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Bool, (bool)(anno.GetTargetValue(obj, member) ?? false));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            anno.SetTargetValue(obj, member, val.ToBool());
            return val;
        }
    }

    public class CommonIntMemberProp : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonIntMemberProp(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member;
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Int, (int)(anno.GetTargetValue(obj, member) ?? 0));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            if (val.Tp == TP.Int) {
                anno.SetTargetValue(obj, member, val.Int());
            } else {
                return Expr.Err("整数属性设值类型错误");
            }

            return val;
        }
    }

    public class CommonIntMemberPropForLong : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonIntMemberPropForLong(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member;
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Int, (int)(anno.GetTargetValue(obj, member) ?? 0));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            if (val.Tp == TP.Int) {
                anno.SetTargetValue(obj, member, (long)val.Int());
            } else {
                return Expr.Err("整数属性设值类型错误");
            }

            return val;
        }
    }

    public class CommonFloatMemberProp : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonFloatMemberProp(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member;
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Float, (decimal)(anno.GetTargetValue(obj, member) ?? 0.0));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            if (val.Tp == TP.Float) {
                anno.SetTargetValue(obj, member, val.Float());
            } else {
                return Expr.Err("小数属性设值类型错误");
            }

            return val;
        }
    }

    public class CommonFloatMemberPropForDouble : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonFloatMemberPropForDouble(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member;
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Float, (decimal)(anno.GetTargetValue(obj, member) ?? 0.0));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            if (val.Tp == TP.Float) {
                anno.SetTargetValue(obj, member, (double)val.Float());
            } else {
                return Expr.Err("小数属性设值类型错误");
            }

            return val;
        }
    }

    public class CommonFloatMemberPropForFloat : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonFloatMemberPropForFloat(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member;
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Float, (decimal)(anno.GetTargetValue(obj, member) ?? 0.0));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            if (val.Tp == TP.Float) {
                anno.SetTargetValue(obj, member, (float)val.Float());
            } else {
                return Expr.Err("小数属性设值类型错误");
            }

            return val;
        }
    }

    public class CommonStringMemberProp : Prop {
        public BindAnno anno;
        public MemberInfo member;

        public CommonStringMemberProp(Obj obj, BindAnno anno, MemberInfo member) : base(obj) {
            this.anno = anno;
            this.member=member;
        }
        public override Expr Qget(Ctx? ctx = null) {
            return new Expr(TP.Str, (string)(anno.GetTargetValue(obj, member) ?? ""));
        }

        public override Expr Qset(Expr val, Ctx? ctx = null) {
            if (anno.noSetter) {
                return Expr.Err("成员(" + anno.attr + ")为只读成员，不允许设值");
            }
            if (val.Tp == TP.Str) {
                anno.SetTargetValue(obj, member, val.Str());
            } else {
                return Expr.Err("字符串属性设值类型错误");
            }

            return val;
        }
    }





}
